package com.growthgate.bootcommon.util;

import org.apache.commons.lang3.time.FastDateFormat;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;

/**
 * DateUtils
 *
 * @author GrowthGate
 * @date 2019/11/1
 */
public class DateUtils {

    /**
     * yyyy-MM-dd HH:mm:ss.SSS
     */
    public static final String FULL_DATETIME = "yyyy-MM-dd HH:mm:ss.SSS";
    /**
     * yyyy年MM月dd日 HH时mm分ss秒SSS毫秒
     */
    public static final String CHINESE_FULL_DATETIME = "yyyy年MM月dd日 HH时mm分ss秒SSS毫秒";
    /**
     * yyyy-MM-dd HH:mm:ss
     */
    public static final String DATETIME = "yyyy-MM-dd HH:mm:ss";
    /**
     * yyyy年MM月dd日 HH时mm分ss秒
     */
    public static final String CHINESE_DATETIME = "yyyy年MM月dd日 HH时mm分ss秒";
    /**
     * yyyy-MM-dd HH:mm
     */
    public static final String DATETIME_NO_SECOND = "yyyy-MM-dd HH:mm";
    /**
     * yyyy年MM月dd日 HH时mm分
     */
    public static final String CHINESE_NO_SECOND = "yyyy年MM月dd日 HH时mm分";
    /**
     * yyyy-MM-dd
     */
    public static final String DATE = "yyyy-MM-dd";
    /**
     * yyyy年MM月dd日
     */
    public static final String CHINESE_DATE = "yyyy年MM月dd日";
    /**
     * yyyy/M/d
     */
    public static final String SPECIAL_DATE = "yyyy/M/d";
    /**
     * HH:mm:ss
     */
    public static final String TIME = "HH:mm:ss";
    /**
     * HH时mm分ss秒
     */
    public static final String CHINESE_TIME = "HH时mm分ss秒";
    /**
     * HH:mm
     */
    public static final String TIME_NO_SECOND = "HH:mm";
    /**
     * HH时mm分
     */
    public static final String CHINESE_TIME_NO_SECOND = "HH时mm分";

    /**
     * Date转String
     *
     * @param date    日期
     * @param pattern 日期格式
     * @return 日期字符串
     */
    @Nullable
    @Contract("null, _ -> null")
    public static String format(@Nullable Date date, @NotNull String pattern) {
        if (date == null) {
            return null;
        }
        return FastDateFormat.getInstance(pattern).format(date);
    }

    /**
     * Date转String
     *
     * @param date    日期
     * @param pattern 日期格式
     * @return 日期字符串
     */
    @Nullable
    @Contract("null, _ -> null")
    public static String toString(@Nullable Date date, @NotNull String pattern) {
        return format(date, pattern);
    }

    /**
     * String转Date
     *
     * @param date    日期字符串
     * @param pattern 日期格式
     * @return 日期
     * @throws ParseException look {@link FastDateFormat#parse(String)}
     */
    @Nullable
    @Contract("null, _ -> null")
    public static Date parse(@Nullable String date, @NotNull String pattern) throws ParseException {
        if (date == null) {
            return null;
        }
        return FastDateFormat.getInstance(pattern).parse(date);
    }

    /**
     * String转Date
     *
     * @param date    日期字符串
     * @param pattern 日期格式
     * @return 日期
     * @throws ParseException look {@link FastDateFormat#parse(String)}
     */
    @Nullable
    @Contract("null, _ -> null")
    public static Date toDate(@Nullable String date, @NotNull String pattern) throws ParseException {
        return parse(date, pattern);
    }

    /**
     * 将“日期 - 日期”字符串拆分为多个日期
     *
     * @param dateStr “日期 - 日期”字符串
     * @param pattern 日期格式
     * @return 日期数组
     * @throws ParseException look {@link FastDateFormat#parse(String)}
     */
    @NotNull
    public static Date[] split(@NotNull String dateStr, @NotNull String pattern) throws ParseException {
        return split(dateStr, " - ", pattern);
    }

    /**
     * 将字符串拆分为多个日期
     *
     * @param dateStr        字符串
     * @param separatorChars 分隔符
     * @param pattern        日期格式
     * @return 日期数组
     * @throws ParseException look {@link FastDateFormat#parse(String)}
     */
    @NotNull
    public static Date[] split(@NotNull String dateStr, @NotNull String separatorChars, @NotNull String pattern) throws ParseException {
        String[] array = dateStr.split(separatorChars);
        Date[] dates = new Date[array.length];
        for (int i = 0; i < array.length; i++) {
            dates[i] = parse(array[i], pattern);
        }
        return dates;
    }

    @NotNull
    private static Date getStart(@NotNull Date date, int field) {
        return org.apache.commons.lang3.time.DateUtils.truncate(date, field);
    }

    @NotNull
    private static Date getEnd(@NotNull Date date, int field) {
        Date tempDate = org.apache.commons.lang3.time.DateUtils.ceiling(date, field);
        return org.apache.commons.lang3.time.DateUtils.addMilliseconds(tempDate, -1);
    }

    /**
     * 获取指定时间那一秒开始的时间
     *
     * @param date 指定时间
     * @return 指定时间那一秒开始的时间
     */
    @NotNull
    public static Date getSecondStart(@NotNull Date date) {
        return getStart(date, Calendar.SECOND);
    }

    /**
     * 获取指定时间那一秒结束的时间
     *
     * @param date 指定时间
     * @return 指定时间那一秒结束的时间
     */
    @NotNull
    public static Date getSecondEnd(@NotNull Date date) {
        return getEnd(date, Calendar.SECOND);
    }

    /**
     * 获取指定时间那一分钟开始的时间
     *
     * @param date 指定时间
     * @return 指定时间那一分钟开始的时间
     */
    @NotNull
    public static Date getMinuteStart(@NotNull Date date) {
        return getStart(date, Calendar.MINUTE);
    }

    /**
     * 获取指定时间那一分钟结束的时间
     *
     * @param date 指定时间
     * @return 指定时间那一分钟结束的时间
     */
    @NotNull
    public static Date getMinuteEnd(@NotNull Date date) {
        return getEnd(date, Calendar.MINUTE);
    }

    /**
     * 获取指定时间那一小时开始的时间
     *
     * @param date 指定时间
     * @return 指定时间那一小时开始的时间
     */
    @NotNull
    public static Date getHourStart(@NotNull Date date) {
        return getStart(date, Calendar.HOUR_OF_DAY);
    }

    /**
     * 获取指定时间那一小时结束的时间
     *
     * @param date 指定时间
     * @return 指定时间那一小时结束的时间
     */
    @NotNull
    public static Date getHourEnd(@NotNull Date date) {
        return getEnd(date, Calendar.HOUR_OF_DAY);
    }

    /**
     * 获取今天开始的时间
     *
     * @return 今天开始的时间
     */
    @NotNull
    public static Date getTodayStart() {
        return getDayStart(new Date());
    }

    /**
     * 获取今天结束的时间
     *
     * @return 今天结束的时间
     */
    @NotNull
    public static Date getTodayEnd() {
        return getDayEnd(new Date());
    }

    /**
     * 获取指定时间那一天开始的时间
     *
     * @param date 指定时间
     * @return 指定时间那一天开始的时间
     */
    @NotNull
    public static Date getDayStart(@NotNull Date date) {
        return getStart(date, Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取指定时间那一天结束的时间
     *
     * @param date 指定时间
     * @return 指定时间那一天结束的时间
     */
    @NotNull
    public static Date getDayEnd(@NotNull Date date) {
        return getEnd(date, Calendar.DAY_OF_MONTH);
    }

    /**
     * 获取指定时间那一月开始的时间
     *
     * @param date 指定时间
     * @return 指定时间那一月开始的时间
     */
    @NotNull
    public static Date getMonthStart(@NotNull Date date) {
        return getStart(date, Calendar.MONTH);
    }

    /**
     * 获取指定时间那一月结束的时间
     *
     * @param date 指定时间
     * @return 指定时间那一月结束的时间
     */
    @NotNull
    public static Date getMonthEnd(@NotNull Date date) {
        return getEnd(date, Calendar.MONTH);
    }

    /**
     * 获取指定时间那一年开始的时间
     *
     * @param date 指定时间
     * @return 指定时间那一年开始的时间
     */
    @NotNull
    public static Date getYearStart(@NotNull Date date) {
        return getStart(date, Calendar.YEAR);
    }

    /**
     * 获取指定时间那一月结束的时间
     *
     * @param date 指定时间
     * @return 指定时间那一月结束的时间
     */
    @NotNull
    public static Date getYearEnd(@NotNull Date date) {
        return getEnd(date, Calendar.YEAR);
    }

    /**
     * 获取两个日期相差的天数
     *
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 天数
     */
    public static int getDiffDay(@NotNull Date startDate, @NotNull Date endDate) {
        return (int) ((endDate.getTime() - startDate.getTime()) / (60 * 60 * 24 * 1000));
    }

    /**
     * 返回增加指定天数的日期
     *
     * @param date   日期
     * @param amount 增加天数
     * @return 增加后的日期
     */
    @NotNull
    public static Date addDays(@NotNull Date date, int amount) {
        return org.apache.commons.lang3.time.DateUtils.addDays(date, amount);
    }

}